package com.darwin.darwinNet
{
	import com.adobe.serialization.json.JSON;
	import com.darwin.darwinNet.events.NetEvent;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.utils.getTimer;

	/**
	 * socket 通讯类 
	 * @author vanCopper
	 * 
	 */	
	public class FSocket extends Socket
	{
		private var tempTime:Number = 0;
		//TODO 心跳 需协商 心跳命令
		//TODO 20秒连接超时 
		public function FSocket()
		{
			super();
			this.endian = Endian.LITTLE_ENDIAN;
			init();
		}
		
		private function init():void
		{
			addEventListener(Event.CONNECT,onConnect);
			addEventListener(ProgressEvent.SOCKET_DATA,onSocketData);
			addEventListener(Event.CLOSE,onClose);
			addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurity);
			addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
		}
		
		private var _sendDataList:Vector.<ByteArray> = new Vector.<ByteArray>();
		/**
		 * 发送数据 
		 * 
		 */		
		private function sendData():void
		{
			if(_connected)
			{
				while(_sendDataList.length)
				{
					var dataByteArray:ByteArray = _sendDataList.shift() as ByteArray;
//					this.endian = Endian.LITTLE_ENDIAN;
					dataByteArray.endian = Endian.LITTLE_ENDIAN;
					this.writeBytes(dataByteArray);
					this.flush();
					trace("sendComplete",dataByteArray.length);
				}
			}else
			{
				retrySocket();
			}
		}
		
		/**
		 * 
		 * 当前socket是否已连接 
		 */		
		private var _connected:Boolean = false;
		private function onConnect(e:Event):void
		{
			_connected = true;
			sendData();//发送数据
		}
		
		private function onClose(e:Event):void
		{
			if(retrySocket() == false)
			{
				var socketEvent:NetEvent = new NetEvent(NetEvent.NET_FSOCKET_CLOSE);
				dispatchEvent(socketEvent);
			}
		}
		
		private function onSecurity(e:SecurityErrorEvent):void
		{
			retrySocket();
		}
		
		private function ioErrorHandler(e:IOErrorEvent):void
		{
			retrySocket();
		}
		
		/**
		 * 重新连接 
		 * @return 
		 * 
		 */		
		private function retrySocket():Boolean
		{
			if(_portIndex >= _portList.length - 1)
			{
				return false;
			}else{
				_portIndex += 1;
				_port = _portList[_portIndex];
				connect(_host,_port);
				return true;
			}
			return false;
		}
		
		private var _readHead:Boolean = false;
		private var _cLength:int = 0;
		private var _cCmd:int = 0;
		private var _cRes:int = 0;
		private var _cData:ByteArray;
		public var netStruct:INetStruct;
		/**
		 * 数据处理 
		 * @param e
		 * 
		 */		
		private function onSocketData(e:ProgressEvent):void
		{
			trace("onSocketData>>>>>>>>>>>>>>>>");
			var completeEvent:NetEvent;
			if(isStructMessage)
			{
				var structAnalyzer:StructAnalyzer = new StructAnalyzer();
				var byte:ByteArray = new ByteArray();
				this.readBytes(byte,0,this.bytesAvailable);
				var structData:Object = structAnalyzer.decode(byte,netStruct);
				completeEvent = new NetEvent(NetEvent.NET_COMPLETE,structData);
				dispatchEvent(completeEvent);
			}else
			{
				tempTime = new Date().time - tempTime;
				while(this.bytesAvailable > 0)
				{//判断socket中是否有数据
					if(!_readHead)
					{//如果没读过头信息
						if(this.bytesAvailable >=8)
						{//是否有足够头信息
							_cLength = this.readUnsignedInt();
							_cCmd = this.readUnsignedShort();
							_cRes = this.readUnsignedShort();
							_readHead = true;
						}else{
							break;
						}
					}
					if(_readHead)
					{//如果已读过头信息
						if(this.bytesAvailable >= _cLength)
						{//如果可读信息大等于包长度
							if(_cLength > 0)
							{
								_cData = new ByteArray();
								this.readBytes(_cData,0,_cLength);
								if(_cRes != 0)
								{
									_cData.uncompress();
								}
								//发送数据到Net
								var result:String = _cData.readUTFBytes(_cData.length);
								var r1:RegExp = /([^\w^\"])(\d{18,})([^\w^\"])/g;
								result = result.replace(r1,"$1\"$2\"$3");
								if(result=="")return;
								var data:Object = com.adobe.serialization.json.JSON.decode(result);
								trace("return......" + result);
								trace("服务器响应时间延迟:" + tempTime + "ms\n");
								completeEvent = new NetEvent(NetEvent.NET_COMPLETE,data);
								dispatchEvent(completeEvent);
								
							}else{
								_cData = null;
							}
							_readHead = false;
						}else{
							break;
						}
					}
				}
			}
		}
		
		/**
		 * socket 连接地址 
		 */		
		private var _host:String = "";
		/**
		 * socket 连接端口 
		 */		
		private var _port:int = 0;
		
		/**
		 * socket连接端口列表 
		 */		
		private var _portList:Array = [];
		/**
		 * 端口索引 
		 */		
		private var _portIndex:int = 0;
		
		/**
		 * 连接socket
		 * @param host socket地址
		 * @param port socket端口，多个端口用 | 分隔
		 * 
		 */
		public function connectMulti(host:String,port:String):void
		{
			_host = host;
			_portList = port.split("|");
			_port = _portList[_portIndex];
			connectServer(host,_port);
		}
		
		private function connectServer(host:String,port:int):void
		{
			super.connect(host,port);
//			if(timer != null) timer.removeEventListener(TimerEvent.TIMER,onTimer);
//			timer = new Timer(6000,1);
//			timer.addEventListener(TimerEvent.TIMER,onTimer);
//			timer.start();
		}
		
		/**
		 * 发送数据 
		 * @param cmd
		 * @param data
		 * @param res
		 * 
		 */		
		public function send(cmd:String,data:ByteArray,res:int = 0):void
		{
			tempTime = new Date().time;
			var _leng:uint = data.length;
			var _sendByteArray:ByteArray = new ByteArray();
			_sendByteArray.endian = Endian.LITTLE_ENDIAN;
			_sendByteArray.writeUnsignedInt(_leng);
			_sendByteArray.writeShort(int(cmd));
			_sendByteArray.writeShort(res);
			_sendByteArray.writeBytes(data);
			_sendDataList.push(_sendByteArray);
			sendData();//发送数据
		}
		
		//是否为结构体通讯协议
		public var isStructMessage:Boolean = false;
		/**
		 * 发送结构体 
		 * @param data
		 * 
		 */		
		public function sendStruct(data:ByteArray):void
		{
			_sendDataList.push(data);
			sendData();
		}
		
	}
}